#include "App.h"





D3DApp::D3DApp()
{
	_hInst = nullptr;
	_hWnd = nullptr;
	_featureLevel = D3D_FEATURE_LEVEL_11_0;
	_pd3dDevice = nullptr;
	_pd3dDevice1 = nullptr;
	_pImmediateContext = nullptr;
	_pImmediateContext1 = nullptr;
	_pSwapChain = nullptr;
	_pSwapChain1 = nullptr;
	_pRenderTargetView = nullptr;
	_pDepthStencilTexture = nullptr;
	_pDepthStencilView = nullptr;
}

D3DApp::~D3DApp()
{
	if (_pImmediateContext) 
		_pImmediateContext->ClearState();
	if (_pRenderTargetView) _pRenderTargetView->Release();
	if (_pSwapChain1) 
		_pSwapChain1->Release();
	if (_pSwapChain) 
		_pSwapChain->Release();
	if (_pImmediateContext1) 
		_pImmediateContext1->Release();
	if (_pImmediateContext) 
		_pImmediateContext->Release();
	if (_pd3dDevice1) 
		_pd3dDevice1->Release();
	if (_pd3dDevice) 
		_pd3dDevice->Release();
	if (_pDepthStencilTexture) 
		_pDepthStencilTexture->Release();
	if (_pDepthStencilView) 
		_pDepthStencilView->Release();
}

bool D3DApp::InitDevice(HINSTANCE hInstance, HWND hWnd)
{
	_hInst = hInstance;
	_hWnd = hWnd;

	HRESULT hr = S_OK;
	RECT rc;
	GetClientRect(hWnd, &rc);
	UINT width = rc.right - rc.left;
	UINT height = rc.bottom - rc.top;

	UINT createDeviceFlags = 0;
#ifdef _DEBUG
	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, nullptr, 0,
		D3D11_SDK_VERSION, &_pd3dDevice, &_featureLevel, &_pImmediateContext);

	if (FAILED(hr))
	{
		MessageBox(0, L"D3D11CreateDevice Failed.", 0, 0);
		return false;
	}

	IDXGIFactory1* dxgiFactory = nullptr;
	{
		IDXGIDevice* dxgiDevice = nullptr;
		hr = _pd3dDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice));
		if (SUCCEEDED(hr))
		{
			IDXGIAdapter* adapter = nullptr;
			hr = dxgiDevice->GetAdapter(&adapter);
			if (SUCCEEDED(hr))
			{
				hr = adapter->GetParent(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&dxgiFactory));
				adapter->Release();
			}
			dxgiDevice->Release();
		}
	}
	if (FAILED(hr))
	{
		MessageBox(0, L"IDXGIFactory1 Failed.", 0, 0);
		return false;
	}

	// Create swap chain
	IDXGIFactory2* dxgiFactory2 = nullptr;
	hr = dxgiFactory->QueryInterface(__uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgiFactory2));
	if (dxgiFactory2)
	{
		// DirectX 11.1 or later
		hr = _pd3dDevice->QueryInterface(__uuidof(ID3D11Device1), reinterpret_cast<void**>(&_pd3dDevice1));
		if (SUCCEEDED(hr))
		{
			(void)_pImmediateContext->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void**>(&_pImmediateContext1));
		}

		DXGI_SWAP_CHAIN_DESC1 sd = {};
		sd.Width = width;
		sd.Height = height;
		sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		sd.SampleDesc.Count = 1;
		sd.SampleDesc.Quality = 0;
		sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		sd.BufferCount = 1;

		hr = dxgiFactory2->CreateSwapChainForHwnd(_pd3dDevice, hWnd, &sd, nullptr, nullptr, &_pSwapChain1);
		if (SUCCEEDED(hr))
		{
			hr = _pSwapChain1->QueryInterface(__uuidof(IDXGISwapChain), reinterpret_cast<void**>(&_pSwapChain));
		}

		dxgiFactory2->Release();
	}
	else
	{
		// DirectX 11.0 systems
		DXGI_SWAP_CHAIN_DESC sd = {};
		sd.BufferCount = 1;
		sd.BufferDesc.Width = width;
		sd.BufferDesc.Height = height;
		sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		sd.BufferDesc.RefreshRate.Numerator = 60;
		sd.BufferDesc.RefreshRate.Denominator = 1;
		sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		sd.OutputWindow = hWnd;
		sd.SampleDesc.Count = 1;
		sd.SampleDesc.Quality = 0;
		sd.Windowed = TRUE;

		hr = dxgiFactory->CreateSwapChain(_pd3dDevice, &sd, &_pSwapChain);
	}

	dxgiFactory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER);
	dxgiFactory->Release();

	if (FAILED(hr))
		return hr;

	ID3D11Texture2D* pBackBuffer = nullptr;
	hr = _pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&pBackBuffer));
	if (FAILED(hr))
		return false;

	hr = _pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &_pRenderTargetView);
	pBackBuffer->Release();
	if (FAILED(hr))
		return false;

	D3D11_TEXTURE2D_DESC depthStencil{ 0 };
	depthStencil.Width = width;
	depthStencil.Height = height;
	depthStencil.MipLevels = 1;
	depthStencil.ArraySize = 1;
	depthStencil.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	depthStencil.SampleDesc.Count = 1;
	depthStencil.SampleDesc.Quality = 0;
	depthStencil.Usage = D3D11_USAGE_DEFAULT;
	depthStencil.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	depthStencil.CPUAccessFlags = 0;
	depthStencil.MiscFlags = 0;

	_pd3dDevice->CreateTexture2D(&depthStencil, 0, &_pDepthStencilTexture);
	_pd3dDevice->CreateDepthStencilView(_pDepthStencilTexture, 0, &_pDepthStencilView);

	_pImmediateContext->OMSetRenderTargets(1, &_pRenderTargetView, _pDepthStencilView);

	D3D11_VIEWPORT vp;
	vp.Width = (FLOAT)width;
	vp.Height = (FLOAT)height;
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	_pImmediateContext->RSSetViewports(1, &vp);

	return true;
}

bool D3DApp::Init()
{
	return false;
}

bool D3DApp::Clean()
{
	return true;
}

void D3DApp::OnSize(int width, int height)
{
	if (_pImmediateContext != nullptr)
	{
		D3D11_VIEWPORT vp;
		UINT num = 1;
		_pImmediateContext->RSGetViewports(&num, &vp);

		vp.Width = (FLOAT)width;
		vp.Height = (FLOAT)height;
		vp.MinDepth = 0.0f;
		vp.MaxDepth = 1.0f;
		vp.TopLeftX = 0;
		vp.TopLeftY = 0;
		_pImmediateContext->RSSetViewports(1, &vp);

	}
}

extern D3DApp &App;

HWND InitWindow(HINSTANCE hInstance, int nCmdShow);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	HWND hWnd = InitWindow(hInstance, nCmdShow);
	if (hWnd == NULL)
		return 0;

	if (!App.InitDevice(hInstance, hWnd))
	{
		return 0;
	}

	if (!App.Init())
	{
		App.Clean();
		return 0;
	}

	__int64 countsPerSec;
	QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec);
	double secondsPerCount = 1.0 / (double)countsPerSec;
	__int64 startCount = 0, currCount = 0;
	QueryPerformanceCounter((LARGE_INTEGER*)&startCount);

	// Main message loop
	MSG msg = { 0 };
	while (WM_QUIT != msg.message)
	{
		if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			QueryPerformanceCounter((LARGE_INTEGER*)&currCount);
			App.Render((currCount - startCount) * secondsPerCount);
			Sleep(10);
		}
	}

	App.Clean();

	return (int)msg.wParam;
}




HWND InitWindow(HINSTANCE hInstance, int nCmdShow)
{
	WNDCLASS wc;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_APPLICATION);
	wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.lpszMenuName = nullptr;
	wc.lpszClassName = L"Directx11Study";
	if (!RegisterClass(&wc))
		return NULL;

	RECT rc = { 0, 0, 800, 600 };
	AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
	HWND hWnd = CreateWindow(L"Directx11Study", L"Direct3D 11 Study",
		WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top,
		nullptr, nullptr, hInstance, nullptr);
	if (!hWnd)
		return NULL;

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	return hWnd;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);
		break;

	case  WM_SIZE:
	{
		int width = LOWORD(lParam);
		int height = HIWORD(lParam);
		App.OnSize(width, height);
	}
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}

	return 0;
}

















